#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>

// 栈的生长方向
// int a; int b; printf("%p,%p",a,b);

// 编译器理解:
// 1.站在编译器的角度,指针就是一个变量,除此之外啥也不是.不管是多少个*,对c++编译器来讲,只会分配4个字节内存.
// 2.在使用的时候,才会考虑是二维的还是多维的.
// 指针的数据类型就是它所指向内存空间的数据类型,它指向的内存空间的数据类型不一样,导致步长不一样.
// 数据类型的本质就是固定内存块的大小的别名,数据类型不一样,步长就不一样.指向指向的数据类型不一样,它能跳一样吗?
//
int getbuf01(char *p);// int getbuf01(char* p)怎样理解
int getbuf02(char **p);// getchar(char* *p)
int getbuf03(char (*p)[]);
int getbuf04(char *****p);
int getbuf05(char p[10][20]);

// 静态存储区数据返回上层调用函数
// 全局变量返回上层调用函数
// stack变量返回给上层函数
char g_buf[10];
char * Demo_Number2String_const(int i)
{
    if(i == 1)
    {
        return "one";
    }
    else if(i == 2)
    {
        return "two";
    }
    else if(i == 3)
    {
        return "three";
    }
    else
    {
        return "err";
    }
}

char *Demo_Number2String_globle(int i)
{
    memset(g_buf,0,sizeof(g_buf));
    if(i == 1)
    {
        strcpy(g_buf,"one");
    }
    else if(i == 2)
    {
        strcpy(g_buf,"two");
    }
    else if(i == 3)
    {
        strcpy(g_buf,"three");
    }
    else
    {
        strcpy(g_buf,"err");
    }
    return g_buf;
}

char * Demo_Number2String_stack(int i)
{
    char tmp[10];
    memset(tmp,0,sizeof(tmp));
    if(i == 1)
    {
        strcpy(tmp,"one");
    }
    else if(i == 2)
    {
        strcpy(g_buf,"two");
    }
    else if(i == 3)
    {
        strcpy(g_buf,"three");
    }
    else
    {
        strcpy(g_buf,"err");
    }
    return tmp;
}
int main_onePointer()
{
    int rv = 0;
    char *pTmp = NULL;
    char buf[20];
    printf("_Demo_Number2String_const(1)--->%s \n", Demo_Number2String_const(1));
    printf("_Demo_Number2String_globle(2)--->%s \n", Demo_Number2String_globle(2));
    printf("_Demo_Number2String_stack(3)--->%s \n", Demo_Number2String_stack(3));
    return rv;
}
// 易错模型
void test4()
{
    char *p1 = NULL;// 没有内存哪有指针
    //    p1 = 0x77;
    strcpy(p1,"abcd");// strcpy(NULL,"abcd");//*(NULL) = 0;
    *p1= 0;

}
// 把实参取地址传给形参
int getFileLen(int *p)
{
    *p = 100;// p是a的地址,*谁的地址,就是间接修改谁
    // 形参取间接的修改实参的值,*p放在=号的左边
}

int main()
{
    system("chcp 65001");
    //    main_onePointer();
    //    test4();
#if 0
    int a = 0;
    int b = 0;
    printf("%p,%p\n",&a,&b);
    printf("%d,%d\n",&a,&b);
    if(&a > &b)
    {
        printf("_开口向下\n");// 栈的高地址在上
    }
    else
    {
        printf("_开口向上\n");
    }
    char *p1 = (char *)malloc(100);// 开口方向向上的
    char *p2 = (char *)malloc(100);// 方向向上的
    printf("%p,%p\n",p1,p2);
    printf("%d,%d\n",p1,p2);
    if(p1 > p2)
    {
        printf("_开口向下\n");// 堆区 的高地址在下
    }
    else
    {
        printf("_开口向上\n");
    }
    // 堆栈的生长方向和内存存放方向是两个不同的概念
    // 堆栈的生长方向,栈的开口向下,顶部放高地址, 堆区的开口向上,先放底部,存放数据的

    // 指针铁律
    // 1.指针是一种数据类型,指针就是一种变量,占有4个字节内存空间,保存内存地址.
    int *p3 = NULL;
    char *p4 =NULL;
    // 测量指针变量占有的内存空间大小
    printf("sizeof(int *): %d, sizeof(char *): %d\n",sizeof(p3),sizeof(p4));

    // 2.操作内存:写内存和读内存
    int a = 10;
    int *p5;
    a = 20;// 直接修改
    p5 = &a;
    *p = 11;// *p操作 : *(谁的地址) 就是去间接的修改谁,*(&a)就是去间接的修改 -->*p 放在= 的左边.就是去 写内存
    // *p 放在= 右边,从内存里面读数据
    int c = *p;// 通过p找到内存数据,从p所指向内存空间读数据.

    // *p的意义 : *就像一把钥匙,通过这把钥匙,通过p这个门,打开一个内存块,去操作内存空间

    // 3.指针变量和它指向的内存块是两个不同的概念
    // -->1 : 给p赋值p = 0x111,只会改变指针变量的值,不会改变所指向的内容,p = p +1;
    // -->2 : 给*p 赋值, *p = 'a',不会改变指针变量的值,只会改变所指向的内存块的值
    // -->3 : 左边的*p表示 给内存赋值,在右边,表示取值
    // -->4 : 左边char *p
    // -->5 : 保证所指向的内存块能修改.
    // 4.指针是一种数据类型,是指它所指向的内存空间的数据类型
    // 含义1:指针步长p++,根据它所指的内存空间的数据类型来确定,p++ = (unsigned char)p + sizeof(a);
#endif
#if 0
    char buf1[100] = {0};
    char buf2[100] = {0};
    strcpy(buf1,"abcdefg");
    char *p1 = buf1;
    char *p2 = buf2;

    printf("p1 : %s,p2 : %s\n",p1,p2);
    while (*p1 != '\0')
    {
        *p2++ = *p1++;
    }
    printf("p1 : %s,p2 : %s\n",buf1,buf2);

    // c语言规定:数组名代表数组首元素的地址,首元素类型是int型变量,步长为4
    // c语言规定:&aaa代表整个数组,&aaa +1= 4*10= 40;

    // int aaa[10];
#endif

#if 0
    char *p2 = NULL;
    p2 = (char *)malloc(100);
    if(p2  != NULL)
    {
        free(p2);
        //        p2 = NULL;// 这里属于没有擦屁股没有擦干净
    }
    if(p2  != NULL)// 会当
    {
        free(p2);
        //        p2 = NULL
    }
#endif

#if 0
    // 易犯错误
    {
        char *p1 = 0;
        strcpy(p1,"abcdef");
        strcpy(0,"abcdef");
        strcpy(NULL,"abcd");
    }
    char buf[100];
    char *p = NULL;
    strcpy(buf,"123456789");

    p = &buf[0];
    p = &buf[1];
    p = &buf[2];
    p = &buf[3];
    p = &buf[4];
    p = &buf[5];
    // 给指针变量赋值,就是不断的改变指针的指向.
    for (int i = 0;i < strlen(buf);i++)
    {
        p = &buf[i];
    }
#endif
#if 0
    char *p = (char *)malloc(100);// 分配100字节内存
    strcpy(p,"1234567");

    p = p+2;//
    *p = 'a';
    //    *(p + 2) = 'a';
    // 记得free
    free(p);// 此时会宕机,因为p的指向变了,p的首地址变了,导致不能正常的free

    int i = 0;// 0 "abcd", 写在代码里面的这些量,称为字面量. 1.保存在哪里?不能对字面量&地址,字面量没有地址,没有放在堆栈区,全局区,放在类似于一个区间里面.
#endif
# if 0
    // 技术推演
    // 铁律2:通过*p/*p++来改变变量的值是指针存在的最大意义.
    // 1.两码事:指针变量和它指向的内存块变量
    // 2.条件反射:指针指向某个变量,就是把某个变量地址赋给指针
    // 3.*p间接赋值成立条件:
    // -->1.2个变量(通常一个实参,一个形参)
    // -->2.建立关系,实参取地址赋给形参指针
    // -->3.*p取间接修改实参的值
    int a = 10;
    int *p =NULL;
    // 1.直接修改a的值
    a = 20;
    printf("_直接修改a的值:%d\n",a);
    // 2.间接修改a的值
    p = &a;

    *p = 40;// p是a的地址,*谁的地址,就是间接修改谁
    printf("_间接修改a的值:%d\n",a);
#endif
#if 0
    int a = 10;
    int *p = NULL;
    // 打造这个环境 p = &a;
    getFileLen(&a);
    // 间接赋值重要场合,不是在一个函数里面,而是在多个函数之间,为什么称之为间接赋值.
    // 在项目中就是这样用,间接的修改形参,你是把a的地址传递进去了.
    printf("_函数修改a的值:%d\n",a);
#endif
#if 0
    // 间接赋值成立的三个条件,异常重要
    // 条件1 : 定义实参变量,定义形参变量
    // 条件2: 建立关联,把实参&地址传给形参
    // 条件3: *p形参去间接修改实参的值
    // 应用场景:
    // -->场景1.在一个函数内修改值--> while(*p1 != '\0'){*p2++ = *p1++;}
    // -->场景2.在两个函数之间,多级指针调用

    // 间接赋值条件应用深入分析
    // -->场景1:条件都写在一个函数内,条件1,2,3
    // -->场景2:条件分开了,12写在一个函数之间,3在另一个函数
    // -->3. 1写在一个函数里面   23在同一个函数里
    int a = 10;// 定义变量(通常是实参)
    int *p = NULL;// 定义1个变量(通常是形参)
    // 打造这个环境 p = &a;
    p = &a;// 建立关联,把一个变量的地址,赋给另外一个变量
    getFileLen(p);// 把实参&地址给
    // 间接赋值重要场合,不是在一个函数里面,而是在多个函数之间,为什么称之为间接赋值.
    // 在项目中就是这样用,间接的修改形参,你是把a的地址传递进去了.
    printf("_函数修改a的值:%d\n",a);
    // 指针的精华:间接赋值,就是指针做函数参数把运算结果给甩出来.
    // 主调用函数定义的变量,在被调用函数里面给修改了.
#endif

#if 0
    // 间接赋值的工程意义
    int a = 10;// a可以看成0级指针.*a的值就相当于取出a所指向内存块的数据
    int *p = NULL;
    // 这就是会用了多级指针
    // 用1级指针形参,去间接的修改0级指针实参的值
    // 用2级指针形参,去间接的修改1级指针实参的值
    // 用n级指针形参,去间接的修改n-1级指针实参的值
    getFileLen(&a);
    // 如果把代码分成一个个小模块,慢慢的会变成一层层两套api模型.没有指针做函数参数,哪有接口的封装设计,没有接口的封装设计,哪有模块的划分.就这点,就是c语言特点,其他语言没有的
    // 落脚点就是指针做函数参数.
    // 积累财富库,为什么老员工会横着走路,因为有各种财富库.
    // 学好c语言,站在指针做函数的角度,去使用c语言,
    // 在c++值得做技术推演的,1.是指针做间接赋值,*p, ()2.多态
    // c半壁江山-->*p和另一个半壁江山:回调参数,AOP编程
    // c++做了3,5年到不了这个层次,2级指针都到2年时间了,
    {
        // 这样是修改不了值的
        void getA(int m)
        {
            m = 10;
            return;
        }
        //        相当于
        void getA()
        {
            int m;// 这个int m;局部变量写在里面和写在外面形参是一样的,无论怎么修改,都无法修改外部的值
            m = 10;
            return;
        }

        int a = 1;
        getA(a);
        printf("b:%d\n",a);
    }
#endif

#if 0
    char buf3[2] = {'b','c'};
    printf("buf3: %s\n",buf3);//bc乱码
#endif

#if 0
    // int * 和char *
    // c语言没有字符串string这个类型,c语言通过字符数组来模拟字符串
    // c风格字符串是以零(\0)结尾的字符串.用一个字符数组 +\0表示字符串

    // 字符数组的初始化
    // 1.指定长度,如果长度大,剩余的部分补零
    char buf[100] = {'a','b','c'};
    printf("buf: %s\n",buf);// buf:abc
    char buf1[2] = {'a','b','c'};
    printf("buf1: %s\n",buf1);//buf1:ababc
    char buf2[2] = {'d','b','c'};
    printf("buf2: %s\n",buf2);//buf2:dbababc
    char buf3[2] = {'b','c'};
    printf("buf3: %s\n",buf3);//bcdbababc bc db ab abc
    printf("buf:%d,buf1:%d,buf2:%d,buf3:%d\n",&buf,&buf1,&buf2,&buf3);
    // buf:6356540,buf1:6356538,buf2:6356536,buf3:6356534
    // 栈区,分配顶部为大数,后边是小数,buf1-3存储数据为2字节,
    // 即: 'b','c'-->'d','b'-->'a','b'-->'a','b','c','\0'
    // 造成读取时buf3时,先读取buf3,发现没有\0结尾,就一直读取内存,直到有\0结尾的

    // 2.不指定长度
    char buf4[] = {'a','b','c'};
    // 默认打印buf开始的内存数据,直到\0结尾的数据
    printf("buf4 : %s\n",buf4);//因为,前边分配了内存
    // 变成c语言字符串
    char buf5[] = {'a','b','c','d','\0'};//变成c语言字符串
    printf("buf5: [%s]\n",buf5);
    // 3. 定义方法,通过字符串初始化字符数组,并追加\0
    char buf5[] = "abcd";// 会自动的添加\0
    char str1[] = "abcd";// char[5]
    sizeof(str1);// 求数据类型大小为5
    strlen(str1);//求字符串中字符的个数,不包括\0
    char str2[] = {'a','b','c','d'};// 占4个长度,不包含\0
    printf("str1:%d,str2:%d\n",sizeof(str1),sizeof(str2));
#endif
#if 0
    // sizeof
    char str1[] = "abcd";// char[5]
    printf("str1:%d\n",sizeof(str1));// sizeof是对数组类型进行大小测量,包含了\0
    printf("strlen str1:%d\n",strlen(str1));// 是求字符串的长度,不包括\0
#endif
#if 0
    // 操作数组的方法,下标法和指针法
    void test1()
    {
        char str1[] = "abcd";
        int i = 0;
        for(i = 0;i < strlen(str1);i++)
        {
            printf("%c ",str1[i]);// 数组,str1是指针 // p[]
        }
        printf("\n");
        char *p =NULL;
        p = str1;
        for(i = 0;i < strlen(str1);i++)
        {

            printf("%c ",*(p + i));// *p
        }
        //* 和[] 的区别,本质是什么?
        // *p是我们程序员手工的显示,去利用间接赋值
        // [] 只不过是c++编译器帮我们做了一个*p的操作;
        // []-->*() -->  p[]-->*(p)  --> p[i] -->*(p+i)
        // 本质 str[i]-->str[0+i]-->*(str + i)
        // *(str + i)-->str[i]

        printf("\n");
    }
    //    test1();
#endif
#if 0
    // 测试例子:安装内存四区画示意图
    // 一级指针的内存模型
    char buf[20]="aaaa";// 栈区分配buf 20字节,存放aaaa,其余为0
    // 首先"aaaa"分配内存,在常量区,分配栈区变量,拷贝过去.
    char buf2[] = "bbbb";// 栈区分配buf2 5字节, 存放bbbb \0
    // buf2[] 是内存数据,隐式的分配内存空间
    // 首先"aaaa"分配内存,在常量区,
    char *p1 = "1111";// "1111"放在文字常量区,p1分配在栈区,把"1111"的地址赋值p1
    char p2 = malloc(100);// 堆区分配p2
    strcpy(p2,"2222");// 把"2222"放在文字常量区,把"2222"的地址赋值p2
    // strcpy 是把"2222"拷贝到p2所指向的内存空间里面
    // buf[20] 是内存块,和指针的区别
    // c语言里面,可以在堆上存放字符串,可以在栈上存放字符串,可以在常量区存放字符串.
    // 指针是子弹,函数像枪管,子弹枪管才能发挥它的威力
    // 1.指针ok了,你只是掌握了c语言的半壁江山,
    // 2.另外一部分在函数指针.
#endif

#if 0
    // 自定义字符串拷贝copy函数,字符串from,到to的copy
    void copy_str(char *from,char *to)
    {
#if 0
        for(;*from != '\0';from++,to++)
        {
            *to = *from;
        }
        *to = '\0';// 需要添加\0
#endif
#if 0
        while(*from != '\0')
        {
            *to = *from;
            from++;
            to++;
        }
        *to = '\0';// 需要添加\0
#endif
#if 0
        while(*from != '\0')
        {
            *to++ = *from++;// ++优先级高, i++;先用后加+1
        }
        *to = '\0';// 需要添加\0
#endif
        // while((*to = *from) != '\0'){*to++;from++;}
        // while((*to++ = *from++) != '\0'){;}
        // (*to++ = *from++) != '\0') 这个已经把*to = '\0';把这个已经'\0'拷贝过去了,
        //  != '\0' 就是不等于0 -->!= 0
        while(*to++ = *from++){;}
    }
    char *p = "abcdefg";
    //    char *p2 = NULL;
    char p2[100];
    //    strcpy(p2,"abcdefg");//
    copy_str(p,p2);// c语言调用字符串
    printf("%s\n",p2);
    // 两个函数,使用同一块内存空间,一个例子练习到极致
    //    strcpy();
    //      char * __cdecl strcpy(char * __restrict__ _Dest,const char * __restrict__ _Source);
#endif
#if 0
    // 接触一新的领域,先去接触一些api函数,对于整体把握
    // 铁律3:理解指针必须和内存四区概念相结合
    // -->1.主调用函数/被调用函数:
    // ----->a.主调用函数可把堆区,栈区,全局数据区的内存地址传给被调用函数
    // ----->b.被调用函数只能返回堆区,全局区数据
    // -->2.内存分配方式
    // ----->a.指针做函数参数,是由输入输出特性的

    // 站在内存四区模型和函数调用模型去思考函数-->api接口
    // 在主调用函数分配内存,你要清楚这个内存是在哪有产生的,就会有输入输出.
    // 如果主调用分配内存,把地址传给被调用函数使用这种就是输入特性.
    // copy_str(char *from /*in*/,char *to /*in*/);
    // int getBuffer();// 获取内存空间
    int getBuffer()
    {
        char buf[100];// 这种分配的空间,不能够传出到主函数进行使用,不得不使用malloc进行分配空间了
        char *p = (char *)malloc(100);
    }
    // 怎么把这个地址传出来,可以通过返回值
    char * getBuffer()
    {
        char *p = (char *)malloc(100);
        retrun p;
    }
#endif

#if 0
    // 项目开发字符串模型
    char *str1 = "hello world abcdefg", *str2 = "orl", *str3 = "ooooo";
    char *p = NULL;
    p = strstr(str1,str2);
    printf("strstr: [%s]\n",p);// strstr: [orld abcdefg]
    p = strstr(str1,str3);// 如果是没有的话
    printf("strstr: [%s]\n",p);// strstr: [(null)]
    // strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是，则该函数返回str2在str1中首次出现的地址；否则，返回NULL。
    // 一个项目中多个return,一个入口多个出口,不是个好事情,因为可能会分配很多的堆区空间
#endif
#if 0
    // 题目:char *p = "abcd1111abcd2222abcd3333abcd4444";请你找出字符串abcd的次数
    // 1.需要引入辅助指针变量
    // 2.赋值指针变量和你的操作逻辑之间的关系
    // 检索之前要更改指针位置
    char *p = "abcd1111abcd2222abcd3333abcd4444";
    int ncout = 0;
    do
    {
        p = strstr(p,"abcd");
        if(p == NULL)
        {
            break;
        }
        else
        {
            ncout++;
            p = p + strlen("abcd");// 更改p的位置
        }
    }while(*p != '\0');// 判断结尾
    printf("ncout :[%d]\n",ncout);
#endif

#if 0
    //业务模型和业务测试模型分离-->接口封装和设计第一步,指针作函数参数
    // 训练接口和封装设计能力,是主调用函数分配内存还是被调用函数分配内存
    // 1.定义接口函数(分配内存),实现功能,2.测试函数用例
    // 所有内存地址,通过参数传递
    //模型:int cltClinet_rev(void *handle,unsigned char *buf,int *buflen);
    // 别人给
    int getCount(char *str,char *substr,int *count)
    {
        char *p =str;
        int ncount = 0;
        do
        {
            p = strstr(p,substr);
            if(p == NULL)
            {
                break;
            }
            else
            {
                ncount++;
                p = p + strlen(substr);// 更改p的位置
            }
        }while(*p != '\0');// 判断结尾
        printf("ncount :[%d]\n",ncount);
        *count = num;
        return 0;
    }
    // 测试用例

    char *p = "abcd1111abcd2222abcd3333abcd4444";
    int ncount = 0;
    char *subp = "abcd";
    int ret = getCount(p,subp,&ncount);
    if(ret != 0)
    {
        printf("getCount error : [%d]\n",ret);
    }
    printf("ncount : [%d]\n",ncount);
    // 别人给你分配内存地址,要可用,不然一跑就宕,不要相信别人给你传递的内存地址是可用的.
    // int getCount(char *str,char *substr,int *count)
    // char *p = NULL;
    // int ncount = 0;
    // char *subp = "abcd";
    // int ret = getCount(p,subp,&ncount);
    int getCount1(char *str,char *substr,int *count)
    {
        int rv = 0;
        // 判断内存是否判断拥有
        if(str == NULL || substr == NULL || count == NULL)
        {
            rv = -1; // 越细越好,打印错误码
            printf("getCount1 : str == NULL || substr == NULL || count == NULL err : [%d]\n",rv);
            return rv;
        }

        char *p =str;
        int ncount = 0;
        do
        {
            p = strstr(p,substr);
            if(p == NULL)
            {
                break;
            }
            else
            {
                ncount++;
                p = p + strlen(substr);// 更改p的位置
            }
        }while(*p != '\0');// 判断结尾
        printf("ncount :[%d]\n",ncount);
        *count = num;
        return rv;
    }
#endif

#if 0
    /*测试案例1
    1、有一个字符串开头或结尾含有n个空格（”   abcdefgdddd    ”），欲去掉前后空格，返回一个新字符串。
    要求1：请自己定义一个接口（函数），并实现功能；70分
    要求2：编写测试用例。30分
    int trimSpace(char *inbuf, char *outbuf);
    **/
    // 思路1:求出空格长度,分别拷贝当中的字符串
#if 0
    int trimSpace(char *inbuf /*in*/, char *outbuf/*in*/)
    {
        int erro_msg = 0;
        if(inbuf == NULL || outbuf == NULL)
        {
            erro_msg = -1;
            printf("erro_msg:[%d]\n",erro_msg);
            return erro_msg;
        }
        char *pinbufLeft = inbuf;
        char *pinbufRight = inbuf;
        char *poutbuf= outbuf;
        int buflen = strlen(pinbufLeft);

        // 去除前面空格
        int lenLeft = 0;
        int lenRight = 0;
        //        while (pinbufLeft != '\0')
        //        {
        //            if(*pinbufLeft == ' ')
        //            {
        //                lenLeft++;
        //                pinbufLeft++;
        //                continue;
        //            }
        //            break;
        //        }
        while (*pinbufLeft == ' ')
        {
            lenLeft++;
            pinbufLeft++;
            continue;
        }
        pinbufRight += buflen-1;
        while (*pinbufRight == ' ')
        {
            printf("pinbufRight:[%c]\n",*pinbufRight);
            lenRight++;
            pinbufRight--;
            continue;
        }
        lenRight =  buflen - lenRight;
        while (1)
        {
            // 这种在下面所取的地址是一样的
            //            if(buflen%2 == 0) //abcd
            //            {

            //            }
            //            else
            //            {
            //                //abcde
            //            }

            if(lenLeft <= buflen/2)
            {
                printf("pinbufLeft:[%c]\n",*pinbufLeft);
                *poutbuf++ = *pinbufLeft++;
                lenLeft++;
                continue;
            }

            if(lenRight >= buflen/2)
            {
                printf("pinbufRight:[%c]\n",*pinbufRight);
                *(poutbuf + lenRight-lenLeft) = *pinbufRight--;
                lenRight--;
                continue;
            }
            break;
        }
        return erro_msg;
    }
    //    char *str = "   abcdefgdddd    ";     //strbuf : [abcdeffgdddd]
    //        char *str = "   abcdefHgdddd    ";//strbuf : [abcdeffHgdddd]
    //    char *str = "AabcdefHgdddd    ";        //strbuf : [AabcdefHHgdddd]
    char *str = "    AabcdefHgdddd";        //strbuf : [AabccdefHgdddd]
    char *strbuf[100] = {0};
    int ret = trimSpace(str, strbuf);
    if(ret != 0)
    {
        printf("err : [%d]\n",ret);
    }
    printf("strbuf : [%s]\n",strbuf);
#endif
    int trimSpace(char *inbuf /*in*/, char *outbuf/*in*/)
    {
        int erro_msg = 0;
        if(inbuf == NULL || outbuf == NULL)
        {
            erro_msg = -1;
            printf("erro_msg:[%d]\n",erro_msg);
            return erro_msg;
        }
        char * p = inbuf;
        int len = strlen(inbuf);// 求出字符串的长度
        printf("len:[%d]\n",len);

        int i = 0;

        // 后空格处理,因为已知长度,
        // "ab", strlen("abcd")=2;srt[0] = 'a',str[1]='b'
        // str[len-1] = 'b';
        while (p[len-1] == ' ')//while (*(p+len-1) == ' ')
        {
            len--;// 字符串长度
        }
        printf("_后空格len:[%d]\n",len);
        // 后空格
        while (*p == ' ')//p[0] = *(p+0)
        {
            p = p+1;// 更改p的指向
            len--;// 长度再相减
        }
        printf("_前空格len:[%d]\n",len);
        // 辅助指针指向前空格的末位的字符串.字符串的长度求出来了
        // 拷贝字符串
        while(len)
        {
            printf("while len:[%d]\n",len);

            *outbuf = *p;
            outbuf++;
            p++;
            len--;//拷贝一个就长度少一个.
        }
        return erro_msg;
    }
    char *str = "   abcdefgdddd    ";     //strbuf : [abcdeffgdddd]
    //        char *str = "   abcdefHgdddd    ";//strbuf : [abcdeffHgdddd]
    //    char *str = "AabcdefHgdddd    ";        //strbuf : [AabcdefHHgdddd]
    //            char *str = "    AabcdefHgdddd";        //strbuf : [AabccdefHgdddd]
    char *strbuf[100] = {0};
    int ret = trimSpace(str, strbuf);
    if(ret != 0)
    {
        printf("err : [%d]\n",ret);
    }
    printf("strbuf : [%s]\n",strbuf);
#endif
#if 0
    /*
    2、有一个字符串”1a2b3d4z”,；
    要求写一个函数实现如下功能，
    功能1：把偶数位字符挑选出来，组成一个字符串1。valude；20分
    功能2：把奇数位字符挑选出来，组成一个字符串2，valude 20
    功能3：把字符串1和字符串2，通过函数参数，传送给main，并打印。
    功能4：主函数能测试通过。
    int getStr1Str2(char *souce, char *buf1, char *buf2);
    */
    int getStr1Str2(char *souce, char *buf1, char *buf2)
    {
        int erro_msg = 0;
        if(souce == NULL || buf1 == NULL || buf2 == NULL)
        {
            erro_msg = -1;
            printf("erro_msg:[%d]\n",erro_msg);
            return erro_msg;
        }
        char *p = souce;
        int buf1Len = 0;
        int buf2Len = 0;
        int i = 1;// 第一位
        while (p[0] != '\0')
        {
            if(i%2==0)
            {
                // 偶数
                *(buf1+buf1Len)= *p;
                buf1Len++;
            }
            else
            {
                *(buf2+buf2Len)= *p;
                buf2Len++;
            }
            p = p+1;
            i++;
        }
        return erro_msg;
    }

    char souce[] = "1a2b3d4z";
    char buf1[100] ={0};
    char buf2[100] ={0};
    int ret = getStr1Str2(souce, buf1, buf2);
    if(ret != 0)
    {
        printf("err : [%d]\n",ret);
    }
    printf("souce:[%s],buf1:[%s],buf2[%s]\n",souce,buf1,buf2);
#endif
#if 0
    /*
3、键值对（”key = valude”）字符串，在开发中经常使用；
要求1：请自己定义一个接口，实现根据key获取valude；40分
要求2：编写测试用例。30分
要求3：键值对中间可能有n多空格，请去除空格30分
注意：键值对字符串格式可能如下：
“key1 = valude1”
“key2 =       valude2
“key3  = valude3”
“key4        = valude4”
“key5   =   “
“key6   =“
“key7   =   “
int getKeyByValude(char *keyvaluebuf,  char *keybuf,  char *valuebuf, int * valuebuflen);
“ORACLE_name=itcast”  “ORACLE_name”  itcast
“oracle_pwd=123456”    “oracle_pwd”   123456
“oracle_pwd=        123456”    “oracle_pwd”   123456
*/
    int getKeyByValude(char *keyvaluebuf,  char *keybuf,  char *valuebuf, int *valuebuflen)
    {

        int erro_msg = 0;
        if(keyvaluebuf == NULL || keybuf == NULL || valuebuf == NULL || valuebuflen == NULL)
        {
            erro_msg = -1;
            printf("erro_msg:[%d]\n",erro_msg);
            return erro_msg;
        }

        char *pKey = keyvaluebuf;

        while(*pKey != '=')
        {
            pKey++;
        }
        char *p = keyvaluebuf;
        while (p < pKey)
        {
            if(*p != ' ')
            {
                *keybuf = *p;
                keybuf++;
            }
            p++;
        }

        int len = strlen(keyvaluebuf);

        p = pKey + 1;//*p = '='
        int count = 0;
        while (*p != '\0')
        {
            if(*p != ' ')
            {
                *valuebuf = *p;
                valuebuf++;
                count++;
            }
            p++;
        }
        *valuebuflen = count;
        return erro_msg;
    }

    //    char souce[] = "ORACLE_name=itcast";
    //    char souce[] = "oracle_pwd=123456";
    //char souce[] = "oracle_pwd=        123456";
    char souce[] = "oracle_pwd=         ";
    char buf1[100] ={0};
    char buf2[100] ={0};
    //    “”  “ORACLE_name”  itcast
    //    “”    “oracle_pwd”   123456
    //    “oracle_pwd=        123456”    “oracle_pwd”   123456
    int len = 0;
    int ret = getKeyByValude(souce, buf1, buf2,&len);
    if(ret != 0)
    {
        printf("err : [%d]\n",ret);
    }
    printf("souce:[%s],buf1:[%s],buf2[%s],len :[%d]\n",souce,buf1,buf2,len);
#endif
#if 0
    void getint(int *a)
    {
        *a = 20;
    }
    int i = 0;
    getint(&i);
    printf("[%d]\n",i);
#endif
#if 0
    // 避免眼高手低,*p是指针存在的最大意义,所以就要在项目中用起来.
    // 两头堵模型
    // 思路:去除空格后的字符串长度
    // 去除空格后的有效长度
    int trimSpaceStr(char *p,char *buf2)
    {
        int ncount = 0;
        int i = 0,j = 0;
        j = strlen(p) -1;
        while(isspace(p[i]) && p[i] != '\0')
        {
            i++;
        }

        while(isspace(p[j]) && j >0)
        {
            j--;
        }

        ncount = j-i +1;// abc,i指向a,j指向c,长度3-1+1
        printf("len:[%d]\n",ncount);
        strncpy(buf2,p+i,ncount);
        //  strncpy(buf2,p[i],ncount);// err, strncpy拷贝是是 内存块的
        // strncpy(buf2,&p[i],ncount);
        buf2[ncount] = '\0';
        return 0;
    }
    char *p = "   aaaffdfddf  ";
    char buf2[100] = {0};
    trimSpaceStr(p,buf2);
    printf("buf2:[%s]\n",buf2);

    // char *p = " abcd  ";
    // test(p){*p = 'a';}// 如果在test函数里面修改p的内存数据,导致崩溃,因为内存不能修改,内存分配在文字长两驱了
    // 不要轻易的改变的别人传来的数据(输入特性性的in内存块的内存),自己开辟空间
#endif
#if 0
    // 字符串反转
    char p[] = "abclop";
    char *p1 = p;
    char *p2 = p + strlen(p)-1;
    while (p1 < p2)
    {
        char tmp = *p1;
        *p1 = *p2;
        *p2 = tmp;
        p1++;
        p2--;
    }
    printf("p:abclop, [%s]\n",p);
#endif

#if 0
    // 开发环境很复杂,所以日志信息必须要详细,每个参数,错误都要打印错误.
    int trimSpaceStr(char *p,char *buf2)
    {
        if(p == NULL)
        {
            return -1;
        }

        if(buf == NULL)
        {
            return -2;
        }
    }
#endif

#if 0
    // 指针都是浮云,内存块不是浮云,是实实在在的,不管是多少指针指向它.
    // 在形参中,用辅助指针或辅助变量,接过来传递过来的形参.清晰明了
    char *getKetByValude(char **keyvaluebuf,char *keybuf,int *len)
    {

        int i = 0;
        char *p = (char *)malloc(100);

        printf("%x\n",p);// 打印p地址
        for(;**keyvaluebuf != '\0';i++)
        {
            *p++ = *(*keyvaluebuf)++;
            *len++;// 这里一定是(*len)++;否则是先len++,后取*
        }
        printf("p:[%s]\n",p);// p,改变了,打印不出了字符串了
        free(p);// 此时p的地址已经被改变,所以会宕
    }
#endif
#if 0
    // const专题
    // 指针变量和它所指向的内存空间变量是两个不同的变量
    // 如果是const修饰的是指针变量,则说明指针变量的指向不能被修改
    // 如果是const修饰的是指针变量所指向的内存空间变量,则说明指向的内存空间数据不能被修改
    // 看const是放在*的左边还是右边,看const修饰的是指针变量,还是指向的内存空间变量
    // const char  *p;// 修饰的是*p,
    // char * const p;// 修饰的是p
    const int a = 10;
    int const b = 20;
    // const 修饰a,b 意义相同

    const char *c;// const修饰的是c指向的内存空间
    char * const d;// d是一个常量指针
    const char * const e;
    // const char *p1:修饰的p1的指向空间
    int getStr(const char *p1)
    {
        //        p1[0] = 'a';// error: assignment of read-only location '*p1'
        char aa = 'v';
        p1 = &aa;// 可以修改指向
        return 0;
    }
    // char const *p1 不可以修改指向,可以修改内存空间
    int getStr2(char * const p1)
    {
        p1[0] = 'a';
        char aa = 'v';
        //        p1 = &aa;// error: assignment of read-only parameter 'p1'
        return 0;
    }
    // 都不可以被修改
    int getStr3(char * const p1)
    {
        //        p1[0] = 'a';
        char aa = 'v';
        //        p1 = &aa;// error: assignment of read-only parameter 'p1'
        return 0;
    }
#endif
#if 0
    // 区分输入输出特性,不允许指向,加const
    int a[10];// a是一个指针,a是常量指针,a++不能被修改,为什么不让你a++,因为a的首地址修改了,a是局部变量,在c++编译器中,怎么释放内存这个局部内存块空间,编译器要拿到a的地址,才能够释放内存.
    // 先判断别人函数的地方不正确,应该怎么样,最后才是你的业务模型
#endif

    // ================>二级指针
#if 0
    // 二级指针一般是二维数组
    // 输入输出,是指:在主调用函数分配内存是输入,在被调用内存中分配内存,是输出

    /* 铁律4:应用指针必须和函数调用相结合(指针做函数参数)--->一直存在
 * 编号   指针函数参数/内存分配方式(级别+堆栈)    主调用函数(实参)   被调用函数(形参)   备注
 * 01    1级指针(作输入)  堆区                分配内存          使用              一般应用禁用
 *                      栈区                分配             使用              常用
 *                      int showBuf(char *p);
 *                      int showArray(int *array,int Num);
 * 02    1级指针(作输出)  栈区                使用              结果传出          常用
 *                      {// 改变int变量,
 *                          int getLen(char *pFileName,int *pfileLen){*pfileLen = 10;};
 *                          int len =0;
 *                          char *str[100] ="a.txt";
 *                          getLen(str,&len);
 *                          printf("len:[%d]\n,len");
 *                      }
 *
 * 03    2级指针(作输入)  堆区                分配              使用             一般应用禁用
 *                      栈区                分配              使用             常用
 *                      int main(int argc,char *argv[]);
 *                      int showMatrix(int str[3][4],int line);// 二维字符串数组
 * 04    2级指针(作输出)   堆区               使用              分配           常用,但不建议用.转成02
 *                      int getData(char **data,int *dataLen);
 *                      int getData_free(void *data);
 *                      int getData_Free(void **data);// 避免野指针
 * 05    3级指针(作输出)  堆区                使用               分配            不常用
 *                      int getFileAllLine(char ***content,int *pLine);
 *                      int getFileAllLine_Free(char ***content,int *pLine);
*/
    // 指针做函数参数,问题的实质不是指针,而是看内存块,内存块是一维,二维.
    // -->如果基础类是int变量,则不需要用指针
    // -->如果内存块是一维二维
    // 输出:在被调用函数里面分配内存块,把内存块的首地址给甩出来
#endif

#if 0
    // 二级指针：第一种内存模型
    // 主函数分配二级指针栈区空间
    char *myArray[] = {"aaaa","dddd","cccc","bbbb"};
    //[] 优先级高,所以是数组,自然而言,先看char myArray[] = ["aaaa","bbbb","cccc","dddd"];
    // 首先考虑它是一个数组,指针数组只不过每一个元素是指针而已.

    // 打印数组和排序数组-->指针做函数参数
    char *tmp =NULL;
    for(int i = 0; i < 4 ;i++)
    {
        printf("[%s] ]\n",myArray[i]);
    }

    for(int i = 0; i < 4 ;i++)
    {
        for(int j = i+1;j <4;j++)
        {
            if(strcmp(myArray[i],myArray[j])>0)
            {
                // 交换的是数组元素,数组元素是指针
                tmp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = tmp;
            }
        }
    }
    printf("===============\n");
    for(int i = 0; i < 4 ;i++)
    {
        printf("[%s] ]\n",myArray[i]);
    }
#endif

#if 0
    char *myArray[] = {"aaaa","dddd","cccc","bbbb"};
    int printfArray(char **pArray,int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        /* 假如,传来的指针地址为NULL--> char **pArray = NULL
         * *pArray==*(NULL),会被宕了
        if(*pArray == NULL)
        {
            return -1;
        }
        */
        for(int i = 0; i < num ;i++)
        {
            printf("[%s]\n",pArray[i]);
        }
        return 0;
    }

    int sortArray(char **pArray,int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        char *tmp = NULL;

        for(int i = 0; i < num ;i++)
        {
            for(int j = i+1;j <num;j++)
            {
                if(strcmp(pArray[i],pArray[j])>0)
                {
                    // 交换的是数组元素,数组元素是指针
                    tmp = pArray[i];
                    pArray[i] = pArray[j];
                    pArray[j] = tmp;
                }
            }
        }
        return 0;
    }

    printfArray(myArray,4);
    sortArray(myArray,4);
    printf("===============\n");
    printfArray(myArray,4);
#endif
#if 0
    int a[10];// a代表数组首元素地址
    //    当操作数具有数组类型时，其结果是数组的总字节数。
    printf("%d\n",sizeof(a));// 40 = 10 *4 = 个数*类型，类型为int型 占4字节所以就是 10 *4
    // sizeof(type)-->sizeof(a)) a是什么类型 int [10]--> 4*10

    printf("%d\n",sizeof(&a));// 4,&a代表整个数组元素,&a 表示后跳40个单元
    char *myArray[] = {"aaaa","dddd","cccc","bbbb"};
    // sizeof(myArray) = 个数*类型 = 4* (char *类型) = 4*4，
    printf("sizeof(char *) [%d]\n",sizeof(char *));// 4字节
    printf("sizeof(myArray) [%d]\n",sizeof(myArray));// 16 = 4*4
    printf("sizeof(&myArray) [%d]\n",sizeof(&myArray));// 4
    {
        int num = sizeof(myArray)/sizeof(myArray[0]);
        int num1 = sizeof(myArray)/sizeof(*myArray);
    }
#endif
#if 0
    // 第2种数据类型
    char myArray[10][30] = {"aaaa","dddd","cccc","bbbb"};
    char buf[30];
    for(int i = 0; i < 4; i++)
    {
        printf("%s \n",myArray[i]);
    }
    for(int i = 0; i < 10 ;i++)
    {
        for(int j = i+1;j < 4;j++)
        {
            if(strcmp(myArray[i],myArray[j])>0)
            {
                // 交换的是数组元素,数组元素是指针
                strcpy(buf,myArray[i]);
                strcpy(myArray[i],myArray[j]);
                strcpy(myArray[j],buf);
            }
        }
    }
    printf("========\n");
    for(int i = 0; i < 4; i++)
    {
        printf("%s \n",myArray[i]);
    }
#endif
#if 0
    int printfArray(char **pArray,int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        for(int i = 0; i < num ;i++)
        {
            printf("[%s]\n",pArray[i]);// 调用时会宕
        }
        return 0;
    }
    char myArray[10][30] = {"aaaa","dddd","cccc","bbbb"};
    char buf[30];
    printfArray(myArray,4);// 调用时会宕
#endif
#if 0
    // ok
    int printfArray(char pArray[10][30],int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        for(int i = 0; i < num ;i++)
        {
            printf("[%s]\n",pArray[i]);// 调用时会宕
        }
        return 0;
    }
    // int sortArray(char pArray[][30],int num)
    // int sortArray(char *pArray[30],int num)
    int sortArray(char pArray[10][30],int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        char buf[30];
        for(int i = 0; i < num ;i++)
        {
            for(int j = i+1;j <num;j++)
            {
                if(strcmp(pArray[i],pArray[j])>0)
                {
                    // 交换的是数组元素,数组元素是指针
                    strcpy(buf,pArray[i]);
                    strcpy(pArray[i],pArray[j]);
                    strcpy(pArray[j],buf);
                }
            }
        }
        return 0;
    }
    char myArray[10][30] = {"aaaa","dddd","cccc","bbbb"};
    char buf[30];
    printfArray(myArray,4);
    sortArray(myArray,4);
    printf("===========\n");
    printfArray(myArray,4);
#endif
#if 0
    // 10 是高维,30是低维
    char myArray[10][30] = {"aaaa","dddd","cccc","bbbb"};
#endif
#if 0
    // 第三种指针类型模型
    // int pArray[100] 相当于
    char **pArray = NULL;
    pArray = (char **)malloc(100 * sizeof(char *));
    pArray[0] = (char *)malloc(12 *sizeof(char ));
    pArray[1] = (char *)malloc(200 *sizeof(char ));
    pArray[2] = (char *)malloc(200 *sizeof(char ));
    pArray[3] = (char *)malloc(200 *sizeof(char ));
    strcpy(pArray[0],"dddd");
    strcpy(pArray[1],"cccc");
    strcpy(pArray[2],"bbbb");
    strcpy(pArray[3],"ffff");
    for(int i = 0;i < 4;i++)
    {
        printf("pArray[i] : [%s]\n",pArray[i]);
    }
    char *buf;
    for(int i = 0; i < 4 ;i++)
    {
        for(int j = i+1;j <4;j++)
        {
            if(strcmp(pArray[i],pArray[j])>0)
            {
                // 交换的是数组元素,数组元素是指针
                buf = pArray[i];
                pArray[i] = pArray[j];
                pArray[j] = buf;
            }
        }
    }
    printf("=========\n");
    for(int i = 0;i < 4;i++)
    {
        printf("pArray[i] : [%s]\n",pArray[i]);
    }
    for(int i = 0;i < 4;i++)
    {
        if(pArray[i] != NULL)
        {
            free(pArray[i]);
            pArray[i] = NULL;
        }
    }
    if(pArray != NULL)
    {
        free(pArray);
        pArray = NULL;
    }
#endif
#if 0
    int printfArray(char **pArray,int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        /* 假如,传来的指针地址为NULL--> char **pArray = NULL
         * *pArray==*(NULL),会被宕了
        if(*pArray == NULL)
        {
            return -1;
        }
        */
        for(int i = 0; i < num ;i++)
        {
            printf("[%s]\n",pArray[i]);
        }
        return 0;
    }

    int sortArray(char **pArray,int num)
    {
        // 这里判断传过来的形参地址是否有效,不管是几级指针
        if(pArray == NULL)
        {
            return -1;
        }
        char *tmp = NULL;

        for(int i = 0; i < num ;i++)
        {
            for(int j = i+1;j <num;j++)
            {
                if(strcmp(pArray[i],pArray[j])>0)
                {
                    // 交换的是数组元素,数组元素是指针
                    tmp = pArray[i];
                    pArray[i] = pArray[j];
                    pArray[j] = tmp;
                }
            }
        }
        return 0;
    }

    char **pArray = NULL;
    pArray = (char **)malloc(100 * sizeof(char *));
    pArray[0] = (char *)malloc(12 *sizeof(char ));
    pArray[1] = (char *)malloc(200 *sizeof(char ));
    pArray[2] = (char *)malloc(200 *sizeof(char ));
    pArray[3] = (char *)malloc(200 *sizeof(char ));
    strcpy(pArray[0],"dddd");
    strcpy(pArray[1],"cccc");
    strcpy(pArray[2],"bbbb");
    strcpy(pArray[3],"ffff");
    printfArray(pArray,4);
    printf("=========\n");
    sortArray(pArray,4);
    printfArray(pArray,4);

    for(int i = 0;i < 4;i++)
    {
        if(pArray[i] != NULL)
        {
            free(pArray[i]);
            pArray[i] = NULL;
        }
    }
    if(pArray != NULL)
    {
        free(pArray);
        pArray = NULL;
    }
#endif
#if 0
    /* 题目
有一个字符串符合以下特征（”abcdef,acccd,eeee,aaaa,e3eeeee,sssss,";）,要求写一个函数（接口），输出以下结果
1）	以逗号分割字符串，形成二维数组，并把结果传出；
2）	把二维数组行数运算结果也传出。
strchr(“aa,aa”,’,’ );
请自己定义一个接口（函数）。
要求1：能正确表达功能的要求，定义出接口（函数）（30分）；
要求2：正确实现接口（函数），并实现功能（40分）；
要求3：编写正确的测试用例。（30分）。
 */
    // 输入
    int sprintString(const char *buf1,char c,char buf2[10][30],int *num)
    {
        int ret = 0;
        if(buf1 == NULL || buf2 == NULL || num == NULL )
        {
            return -1;
        }
        char *p1 = NULL;
        char *p2 = NULL;
        int ncount = 0;
        //        char myBuf[1024] = {0};

        // 步骤1 初始化条件,使p1,p2都指向检索字符串的开头
        p1 = buf1;
        p2 = buf1;

        do
        {
            // 步骤2,拷贝字符串, strchr会让p1指针后移,在p1和pTmp之间有一个差值.
            p1 = strchr(p1,c);
            if (p1 == NULL)
            {
                break;// 没有找到串
            }
            else
            {
                //                memset(myBuf,0,sizeof(myBuf));
                //                strncpy(myBuf,p2,p1-p2);// 挖字符串
                //                myBuf[p1-p2] = '\0';
                //                strcpy(buf2[ncount],myBuf);
                strncpy(buf2[ncount],p2,p1-p2);
                buf2[ncount][p1-p2] = '\0';
                // 步骤3 让p1,p2重新初始化,达到检索条件
                p2 = p1 = p1+1;
                ncount++;
            }
        }while(*p1 != '\0');
        *num = ncount;

        return ret;
    }
    char *str = "abcdef,acccd,eeee,aaaa,e3eeeee,sssss,";
    char c = ',';
    char buf[10][30] = {0};
    int num = 0;
    int ret = sprintString(str,c,buf, &num);
    if(ret != 0)
    {
        printf("err : [%d]\n",ret);
        return ret;
    }
    for(int i = 0;i < num; i++)
    {
        printf("buf[%d] : [%s]\n",i,buf[i]);
    }
#endif

#if 0
    // 把字符串练到极致,用两个辅助指针插字符串和在两个主调用和被调用函数里面练习.
    // 数组类型是压死初学者的三座大三之一.
    // 在C语言中,概念不清晰,是产生bug的根源.
    // 概念清晰,练习不到位,也是产生bug的根源
    // c语言深入理解c各种语法现象,才能成为高手
    // char *myAarray[100];malloc(分配内存),
    char **newStr = (char **)malloc(100 * sizeof(char *));
    // char(**),强转,malloc 返回的是char *
    // char myArray[100]
    char *newStr = (char *)malloc(100 * sizeof(char));
    // "ab"
    // char * buf = (char *)malloc(sizeof("ab") + 1);
    // 程序一个入口,多个出口,容易内存泄漏.return 之前应该释放资源.
    // 对付1个入口多个出口的问题
    // -->1. 添加标号 goto,End:
#endif
#if 0
    int sprintString(const char *buf1,char c,char **buf2,int *num)
    {
        int ret = 0;
        if(buf1 == NULL || buf2 == NULL || num == NULL )
        {
            return -1;
        }
        char *p1 = NULL;
        char *p2 = NULL;
        int ncount = 0;
        //        char myBuf[1024] = {0};

        // 步骤1 初始化条件,使p1,p2都指向检索字符串的开头
        p1 = buf1;
        p2 = buf1;

        do
        {
            // 步骤2,拷贝字符串, strchr会让p1指针后移,在p1和pTmp之间有一个差值.
            p1 = strchr(p1,c);
            if (p1 == NULL)
            {
                break;// 没有找到串
            }
            else
            {
                //                memset(myBuf,0,sizeof(myBuf));
                //                strncpy(myBuf,p2,p1-p2);// 挖字符串
                //                myBuf[p1-p2] = '\0';
                //                strcpy(buf2[ncount],myBuf);
                strncpy(buf2[ncount],p2,p1-p2);
                buf2[ncount][p1-p2] = '\0';
                // 步骤3 让p1,p2重新初始化,达到检索条件
                p2 = p1 = p1+1;
                ncount++;
            }
        }while(*p1 != '\0');
        *num = ncount;

        return ret;
    }
    // 分配内存
    char ** getMem(int num)
    {
        char **pp = (char **)malloc(num * sizeof(char *));
        if(pp == NULL)
        {
            return NULL;
        }

        for(int i = 0;i < num;i++)
        {
            pp[i] = (char *)malloc(100);
            if(pp[i] == NULL)
            {
                break;
            }
        }
        return pp;
    }

    int getFree(char **pp,int num)
    {
        if(pp == NULL)
        {
            return -1;
        }
        for(int i = 0;i < num; i++)
        {
            if(pp[i] != NULL)
            {
                free(pp[i]);
                pp[i] = NULL;
            }
        }
        free(pp);
        pp = NULL;
        return 0;
    }
    char *str = "abcdef,acccd,eeee,aaaa,e3eeeee,sssss,";
    char c    = ',';
    //    char buf[10][30] = {0};
    char **pp = NULL;
    int numArray = 10;
    pp = getMem(numArray);
    if(pp == NULL)
    {
        return 0;
    }
    int num = 0;
    int ret = sprintString(str,c,pp, &num);
    if(ret != 0)
    {
        printf("err : [%d]\n",ret);
        return ret;
    }
    for(int i = 0;i < num; i++)
    {
        printf("buf[%d] : [%s]\n",i,pp[i]);
    }
    //
    getFree(pp,numArray);
#endif

#if 0
    // 数组概念:
    // -->1.元素类型角度:数组是相同类型的变量的有序集合.
    // -->2.内存角度:联系-->一大片连续内存空间
    // 数组初始化:
    // -->1.数组元素的个数可以显示或者隐式的指定
    // 数组名代表数组首元素的地址,它是个常量,变量本质是内存空间的别名,一旦定义数组,就开始分配内存,内存就固定了,数组名就不能被修改.
    // 数组首元素地址和数组的地址值相等.
    int a[10] = {0};// 数组的首元素地址是a,整个数组的地址是&a;
    // 数组:数组有多长,数组元素类型
    // int array[5] 的类型是int[5] -->变量为array
    // 数组类型 typedef int(Array)[5];Array array ;-->int array[5];
    // 定义一个数组类型
    typedef struct _Teacher
    {
        char name[100];
    }Teacher;
    typedef int MyArrayType[5];// int

    MyArrayType array;// 相当于int array[5];
    for(int i = 0;i < 5;i++)
    {
        array[i] = i;
    }
    for(int i = 0;i < 5;i++)
    {
        printf("array:[%d]\n",array[i]);
    }
#endif
#if 0
    int a= 10;
    int *p =NULL;// 咱们可以用int类型定义指针,存的数据就是int类型,*p = 10;
    p = &a;
    int arr[10];


    // 用数组定义指针,存的数据就是数组
    // 定义一个数组类型
    typedef int MyArrayType[5];
    // 定义数组类型指针
    MyArrayType *pArray = NULL;// 定义了一个数组类型的指针,存的数据就是数组
    pArray = &arr;// 数组类型指针指向整个数组,
    // 操作arr[5]的内存
    for(int i = 0;i < 5;i++)
    {
        (*pArray)[i] = i+1;
    }

    for(int i = 0;i < 5;i++)
    {
        printf("(*pArray)[%d]:[%d]\n",i,(*pArray)[i]);
    }
#endif
#if 0
    // 公式
    Type type;
    Type* mPoint;
    mPoint = &type;
    *mPoint = (Type)T;
    // Type  为int型
    int type;
    int * mPoint;
    mPoint = &type;
    *myPoint = (int)10;
    // Type  为int arr[]型
    int arr[10] = {0};
    //    int (*)[10] arrPoint
    int (*arrPoint)[10] = NULL;
    arrPoint = &arr;
    // 去掉变量去除*就是指针指向的类型
#endif

#if 0
    // 定义一个类型是数组类型
    typedef int MyArrayType[5];
    // int MyArrayType[5]
    // -->1. MyArrayType 是变量
    // -->2.类型呢?:int 和 [5]哪个优先级高,自然先和[5],结合,所以是数组类型,
    // -->3.数组类型存的数据是int型的

    // 定义一个类型,整个类型是数组指针类型
    typedef int (*MyArrayPointType)[5];// []数组下标 ()圆括号 .成员选择（对象） ->指针 从左到右
    // int (*MyArrayType)[5]
    // -->1.变量是MyArrayType
    // -->2.类型?(*)比[] 级别要高所以 是指针,指针的类型是int [],也就是说指针指向int []
    // 定义了一个指针类型,指针指向数组类型
    // 定义了一个指向数组的指针类型-->数组指针
    int arr[5];
    MyArrayPointType myPoint;
    myPoint= &arr;
    // MyArrayType + 1-->步长是数组的长度
    for(int i = 0;i < 5;i++)
    {
        (*myPoint)[i] = i+1;
    }

    for(int i = 0;i < 5;i++)
    {
        printf("(*myPoint)[%d]:[%d]\n",i,(*myPoint)[i]);
    }
#endif
#if 0 // 重点
    // sizeof(long)必须等于(void *)
    int a[10] = {0};
    // 定义一个类型是数组类型
    typedef int MyArrayType[5];
    MyArrayType * myArrarPointType;// 定义数组指针类型
    myArrarPointType = &a;
    // 直接定义数组指针类型
    typedef int (*MyArrayPointType)[5];
    MyArrayPointType  * myArrarPointType1;// 用数组指针类型定义变量
    myArrarPointType1 = &a;
    // myArrarPointType 和 myArrarPointType1 都是 数组指针类型

    // 直接定义数组指针类型
    int (*myArrPoint1)[5];// 定义了myArrPoint1 立马分配4个字节内存,它是指针,相当于指针,分配指针变量的内存
    // int (*myArrPoint1)[5];
    // -->变量是 myArrPoint1,拿出变量来 int (*)[5];
    // -->int (*)[5] -->类型就是指针,指针就是指向一块内存地址,存的就是地址,这块地址的数据类型就是去掉*的类型 就是int [5];
#endif

    // sizeof(int);
#if 0
    int a= 10;
    int a1 = &a;
    int a2 = sizeof(&a);
    printf("&a:[%d],sizeof(&a):a2:[%d]\n",a1,a2);
    printf("&a:[%d]\n",&a);
    printf("sizeof(&a):[%d]\n",sizeof(&a));
    printf("sizeof(&a):[%d]\n",sizeof(int *));
    printf("sizeof(&a):[%d]\n",sizeof(void *));
    char chArr[2] = { 0 };

    int chArr1 = chArr + 1;// chArr 类型是 char []-->char *a
    int chArr11 = &chArr[0] + 1;
    int chArr2 = &chArr + 1;

    const char *chArr4 = (char *)malloc(1);
    memset(chArr4,0,sizeof(chArr4));
    int chArr41 = chArr4 + 1;
    int chArr42 = chArr4 + sizeof(chArr4) -1 + 1;

#endif
#if 0
    //适用于非数组
    // #define _sizeof(T) ((size_t)((T*)0 + 1))
    //适用于数组
    // #define array_sizeof(T) ((size_t)(&T+1)-(size_t)(&T))
    // #define NEWSIZEOF(x)      ((char*)(&x+1)-(char*)&x)
    // 地址 = Type + 1;//
    char a;// 定义char 类型
    a = 'a';// 'a' 字符 存在 区域里面,相当于栈区变量 a 存了一个数是 'a' 00000061
    // --> 'a' + 1 = 'b'
    // a+1;//存的这个数+1--> 和 &a +1-->&a这个空间+1的位置就是 a的结束位置地址 +1 =a的开始地址+ sizeof(a) -1 + 1 =  0060FE9F + 1 -1 + 1 = 0060FE9F + 1
    // sizeof(a):[1],sizeof(&a):[4]
    // a:[00000061],a+1:[00000062],&a:[0060FE9F],&a+1:[0060FEA0]
    // a:[a],       a+1:[b],       &a:[0060FE9F],&a+1:[0060FEA0]

    printf("sizeof(a):[%d],sizeof(&a):[%d]\n",sizeof(a),sizeof(&a));
    // &a+1-->就是地址+1;步长(这一步)就是sizeof(a);
    printf("a:[%p],a+1:[%p],&a:[%p],&a+1:[%p]\n",a,a+1,&a,&a+1);
    printf("a:[%c],a+1:[%c],&a:[%p],&a+1:[%p]\n",a,a+1,&a,&a+1);
    printf("============================\n");

    // Type X;
    // &X-->代表的是整个Type的地址,代表整个空间的地址,
    // 这块多大呢,sizeof(Type);a + 1,这块空间 + 往前 +1

    // 类型就是固定内存块的别名(整个内存块多大) 类型 a;分配这么大的内存
    // int
    int b;
    b = 0;// b变量存的数据是0
    // sizeof(b):[4],sizeof(&b):[4]
    // 0 + 1 = 1
    // b + -->b变量存的数据+1
    // b:[00000000],b+1:[00000001],&b:[0060FE98],&b+1:[0060FE9C]
    // b:[ ],b+1:[],&b:[0060FE98],&b+1:[0060FE9C]

    printf("sizeof(b):[%d],sizeof(&b):[%d]\n",sizeof(b),sizeof(&b));
    // &b+1-->就是地址+1;步长(这一步)就是sizeof(b) + 1;
    printf("b:[%p],b+1:[%p],&b:[%p],&b+1:[%p]\n",b,b+1,&b,&b+1);
    printf("b:[%c],b+1:[%c],&b:[%p],&b+1:[%p]\n",b,b+1,&b,&b+1);
    printf("&b+1 -&b :[%d]\n",&b+1 - &b);// 1

    printf("============================\n");

    char d = 'a';
    char * c = NULL;
    c = &d;
    // sizeof(c):[4],sizeof(&c):[4]
    // c:[0060FE97],&d:[0060FE97],c+1:[0060FE98],&c:[0060FE90],&c+1:[0060FE94]
    // c:[],c+1:[],&c:[0060FE90],&c+1:[0060FE94]

    // c 的类型是char *占4字节, &c-->意思是取出这块内存的开始地址(首地址)

    printf("sizeof(c):[%d],sizeof(&c):[%d]\n",sizeof(c),sizeof(&c));
    // c + 1-->相当于c存的数据 +1--> &d + 1 --> 地址 + 1
    printf("c:[%p],&d:[%p],c+1:[%p],&c:[%p],&c+1:[%p]\n",c,&d,c+1,&c,&c+1);
    printf("c:[%c],c+1:[%c],&c:[%p],&c+1:[%p]\n",c,c+1,&c,&c+1);

    printf("============================\n");
    char e = 'a';// &e 是常量
    char * f = NULL;
    f = &e;
    // f:[0060FE8F],&e:[0060FE8F],f+1:[0060FE90],&f:[0060FE88],&f+1:[0060FE8C]
    printf("f:[%p],&e:[%p],f+1:[%p],&f:[%p],&f+1:[%p]\n",f,&e,f+1,&f,&f+1);
    int g = 10;
    f = &g;
    // f:[0060FE84],&g:[0060FE84],f+1:[0060FE85],&f:[0060FE88],&f+1:[0060FE8C]
    printf("f:[%p],&g:[%p],f+1:[%p],&f:[%p],&f+1:[%p]\n",f,&g,f+1,&f,&f+1);
    // f+1 != &g +1 -->编译器做好运算了,f = 0060FE84 -->f + 1 -->0060FE84 + 1;
    // f:[0060FE84],f+1:[0060FE85],&g:[0060FE84],&g+1:[0060FE88],&f:[0060FE88],&f+1:[0060FE8C]
    printf("f:[%p],f+1:[%p],&g:[%p],&g+1:[%p],&f:[%p],&f+1:[%p]\n",f,f+1,&g,&g +1,&f,&f+1);

    printf("============================\n");

    char * const ch_1 = (char *)malloc(sizeof(char));
    char *b_gogo = NULL;
    printf("b:[%p]",b_gogo+1);

#endif
#if 0
    Sizeof的作用非常简单：求对象或者类型的大小。然而sizeof又非常复杂，它涉及到很多特殊情况，本篇把这些情况分门别类，总结出了sizeof的10个特性：

            (0)sizeof是运算符，不是函数；

            (1)sizeof不能求得void类型的长度；

            (2)sizeof能求得void类型的指针的长度；

            (3)sizeof能求得静态分配内存的数组的长度!

            (4)sizeof不能求得动态分配的内存的大小!

            (5)sizeof不能对不完整的数组求长度；

            (6)当表达式作为sizeof的操作数时，它返回表达式的计算结果的类型大小，但是它不对表达式求值！

            (7)sizeof可以对函数调用求大小，并且求得的大小等于返回类型的大小，但是不执行函数体！

            (8)sizeof求得的结构体(及其对象)的大小并不等于各个数据成员对象的大小之和！

            (9)sizeof不能用于求结构体的位域成员的大小，但是可以求得包含位域成员的结构体的大小！
        #endif
        #if 0 // 多维数组
            int a[10];// a代表数组首元素地址,&a代表整个数组的地址(数组的地址)
    // sizeof(类型);a类型是数组(int [10]),sizeof(&a)-->&a的类型就是取地址,这个地址多长,32位:1111 1111 1111 1111, 1111 1111 1111 1111 -->FFFF FFFF -->1字节相当于FF
    // 32位相当于 4字节,这个地址就是用32位数存的.
    // sizeof(a):[40],sizeof(&a):[4]
    printf("sizeof(a):[%d],sizeof(&a):[%d]\n",sizeof(a),sizeof(&a));

    // int a[10]; -->数组的类型就是int型,也就是4字节,a+1-->a地址+4
    // char a[10];-->数组的类型就是char型,步长也就是1字节,a+1-->a地址+1
    // a:[6356600],a+1:[6356604],&a+1:[63 56640]
    printf("a:[%d],a+1:[%d],&a+1:[%d]\n",a,a+1,&a+1);

    // a:[0060FE78],a+1:[0060FE7C],&a+1:[0060FEA0]
    printf("a:[%p],a+1:[%p],&a+1:[%p]\n",a,a+1,&a+1);
    // &a+1 代表了什么,a是变量,变量的类型就是整个int 数组类型,&a的地址 +1代表的就是地址+整个数组的长度
    int b[3][5];
    // b代表了什么了,b的步长:int b[][],
    b + 1;// 相当于*b+1-->取出b存的数据+1
    printf("b:[%p],b+1:[%p]\n",b,b+1);// b:[0060FE3C],b+1:[0060FE50]
    printf("b:[%d],b+1:[%d],步长:[%d]\n",b,b+1,(int)(b+1)-(int)b);// b:[6356540],b+1:[6356560]
    // 相当于:步长 (int)(b+1)-(int)b):20 = 4*5= sizeof(int)*5
    // b+1 = *b+1= int [5] +1= 相当于int [5]这种数据的步长
    // b的本质就是数组指针,每次往后跳一维的维数.
#endif
#if 0
    // 定义数组指针变量,就是一个指针指向数组,指向低维数组指针
    int a[3][5] = {0};
    int count = 0;
    for(int i = 0;i < 3; i++)
    {
        for(int j = 0;j < 5; j++)
        {
            a[i][j] = ++count;
        }
    }

    int (*myArrP)[5];
    myArrP = a;

    // 打印
    for(int i = 0;i < 3; i++)
    {
        for(int j = 0;j < 5; j++)
        {
            printf("myArrP[%d][%d] = [%d]   ",i,j,myArrP[i][j]);
        }
        printf("\n");
    }
    for(int i = 0;i < 3; i++)
    {
        for(int j = 0;j < 5; j++)
        {
            printf("a     [%d][%d] = [%d]   ",i,j,a[i][j]);
        }
        printf("\n");
    }
    printf("int(myArrP+1) -(int)(myArrP)= [%d]\n",(int)(myArrP+1) -(int)(myArrP));
    printf("myArrP:[%d],(myArrP+1) = [%d]\n",myArrP,(myArrP+1));
    printf("*(myArrP+1) = [%d]\n",*(myArrP+1));
    printf("*(myArrP+1)+1 = [%d]\n",*(myArrP+1)+1);

    // int a[3][5] = {0}; int b[5]; int (*myArrP)[5];
    // a指向b低维数组,sizeof(int)*5
    // char buf[10][30] : buf +1 步长= sizeof(char) * 30
    // 相当于第i行的数组首地址
    // buf+i相当于二级指针,相当于指针指向一维数组,+i,指向不同的一维
    // buf+i 保存就是一维数组b[5]整个数组的地址 &b-->*(&b)-->结果就是b
    // *(buf+i) = 取出一维数组的首地址,开始地址
    // *(buf+i)-->取出低维第i行的首地址 &buf[i]
    // *(buf+i)+j -- >取出低维第i行的第j的地址 &buf[i][j]
    // *(*(buf+i)+j) --> buf[i][j]
    //--->表示形式 int a[3][5],优先级() [] * &
    // 第0行第1列元素地址:    a[0]+1  *a+1      &(a[0][1]) &a[0][1])
    // 第i行第j列元素地址:    a[i]+j  *(a+i)+j  &(a[i][j]) &a[i][j])
    // 第i行第j列元素地址的值: *(a[i]+j) *(*(a+i)+j) a[i][j]
#endif

#if 0
    // 多维数组做函数参数参数退化原因
    // 内存永远是线性,二维模型是人为建立起来的,每次多少.
    // char buf[10];
    // char ch; char *p = &ch
    // -->1.buf(一级指针),代表数组首元素的地址,第一个元素的地址,第一个元素类型是char
    // -->2.&buf(二级指针)代表整个数组的地址
    // char arr[10][10]
    // arr是二级指针,arr代表数组首元素的地址,元素类型是char [10]
    // arr+i相当于和 &buf意思是一样.整个第i行的数组地址

    // 退化-->*P,去除一层指针;数组就是 &a是二级指针,所以退化为一级指针
    // int a[10] ==>int a[] ==> int *a;

    int printfArr(char buf[10][10]);
    // char buf[10][10] ==>退化 char buf[][10]
    int printfArr(char buf[][10]);// buf[] 相当于 *(buf)
    int printfArr(char *(buf)[10]);
    {

    }

#endif
#if 0
    // 指针数组
    char *p1[] = {"123","abc","adf"};// char *p1[3]

    // 二维数组
    char p2[3][4] = {"123","abc","adf"};

    // 手工二维内存
    char **p3 = (char **)malloc(3*sizeof(char *));//
    for(int i = 0; i < 3;i++)
    {
        p3[i] = (char *)malloc(10*sizeof(char *));
        sprintf(p3[i],"%d%d%d",i,i+1,i+2);
    }

    // char *p1[]
    // char (*p2)[];//ok,编译器只会分配4字节内存,至于怎么用是你的事情
    // char (*p2)[] = {"a","b"};//err设定项太多
    // 所有的指针就是为内存服务的  [] 优先级高,先退化[].
    // char *p[3] ==>退化为char *p[]-->相当于char **p;
#endif
#if 0 // 多维数组做函数参数退化  [] 优先级高,先退化[].
    // 1.c语言中只会以机械式的值拷贝的方式传递参数,把实参把值传给形参
    // int func(char a[10],size_t b)-->退化char *a
    // -->原因:高效,c语言处理a[n]的时候,没有办法知道n是几,只知道&n[0]是多少.
    // 二维数组可以看做一维数组
    // 二维数组中的每个元素是一维数组
    // --> 退化-->形式
    //         数组参数               等效的指针参数 void func(N); N = char a[30]
    // 一维数组 char a[30]  指针       char * -->func(char a[30])-->func(char *a)
    // 指针数组 char *[30]  指针的指针  char **
    // 二维数组 char a[2][5] 数组的指针 char (*a)[5]
    // 指针数组 char *[30] // [] 优先级高,先退化[].
    // char *其实是个类型, typedef char * NType
    // NType  a[10]-->退化NTpye *a;
    // #define SIZE_TYPE sizeof(a)/sizeof(*a)
#endif
#if 0
    // 1. 你在哪个框架底下干活-->
    // 2.mfc应用框架/qt
    // 3. linux下,驱动
    //    char * c_keyword[] = {"while","case","static","do"};
    // char * c_keyword[] = {"while","case","static","do",NULL};// ok
    //    char * c_keyword[] = {"while","case","static","do",0};// ok
    char * c_keyword[] = {"while","case","static","do",'\0'};// ok
    // #define NULL 0
    for(int i = 0; c_keyword[i] != NULL; i++)
    {
        printf("%s \n",c_keyword[i]);// 宕机,c_keyword[4]-->取出的串不等于NULL
    }

    // 1 2输入,主调用函数分配内存
    // 1 2做输出,被调用函数分配内存
    // 函数指针做函数参数,正向调用,反向调用(回调函数)
    // -->达到项目开发的要求.
    // char * a(); char (*a2)()// 是一个指针函数 ()[]自左向右,是指针变量

#endif
#if 0
    // 野指针产生问题分析
    // 指针变量和它所指向的内存空间变量是两个不同的概念
    // 1.定义指针时,把指针变量赋值程null-->char *p =NULL;
    // 2.释放内存时,先判断指针变量是否为NULL
    // 3.释放内存时,free().把指针变量重新赋值成NULL.
    // 形参和形参是两个不同的变量

    char *getMemSet(int num)
    {
        char *p = NULL;
        p = (char *)malloc(100 * sizeof(char));
        return p;
    }

    int freeMemSet(char *p)
    {
        if(p == NULL)
        {
            return -1;
        }
        if(p != NULL)
        {
            free(p);
            p = NULL;
        }
        return 0;
    }

    char *p = NULL;
    p = getMemSet(100);

    freeMemSet(p);
    freeMemSet(p);// 宕
    freeMemSet(p);// 宕
#endif
#if 0 // 结构体的定义
    // 这样是定义了一个类型,没有分配内存,固定大小块的内存
    struct Teacher
    {
        char name[64];// 64个字节
        int age;
    };

    struct Teacher t1;// 这样告诉编译器分配内存
    struct Teacher *p = (struct Teacher*)malloc(sizeof(struct Teacher));

    free(p);

    struct Teacher1
    {
        char name[62];// 4字节对齐
        int age;
    };

    struct Teacher1 t2;
    printf("sizeof(t2):[%d]",sizeof(t2));// 68
    struct Teacher2
    {
    };

    struct Teacher2 t3;
    printf("sizeof(t3):[%d]",sizeof(t3));// 0字节

    struct Teacher3
    {
        int age;
    };

    struct Teacher3 t4;
    printf("sizeof(t4):[%d]",sizeof(t4));// 4字节

    // 起别名
    typedef struct _Teacher
    {
        char name;
    }Teacher4;
    struct _Teacher1
    {
        char name;
    }t5,t6;// 直接定义变量
#endif
#if 0
    typedef struct _Teacher
    {
        char name[62];// 4字节对齐
        int age;
    } Teacher;

    Teacher t1;
    t1.age = 10;

    Teacher *t2 = NULL;
    t2 = &t1;
    t2->age;// . 和-> 本质是寻址,操作结构体的成员域,寻每个成员相对于结构体的内存偏移,没有操作内存
    strcpy(t1.name,"abcd");
    Teacher t3;
    t3 = t1;
    printf("[%s],age :[%d]\n",t3.name,t3.age);
    void copyStruct(Teacher *t0,Teacher *from)
    {
        *t0 = *from;
    }
    void copyStruct(Teacher t0,Teacher from)
    {
        t0 = from;// 这样就不成功,这样实参和形参是值拷贝.
    }

    Teacher t4;
    t4.age = 10;
    Teacher t5;
    copyStruct(&t5,&t4);
    printf("age :[%d]\n",t5.age);
#endif
#if 0 // 栈区分配结构体数组
    // 定义一个结构体数组,给结构体数组元素赋值,给结构体赋值排序,打印
    typedef struct _Teacher
    {
        char name[62];// 4字节对齐
        int age;
    } Teacher;
    Teacher tArray[3];

    int  printfTArray(Teacher *tArray,int num)
    {
        for (int i = 0; i < num; ++i)
        {
            printf("%d : age [%d]\n",i+1,tArray[i].age);
        }
        return 0;
    }
    int  sortTArray(Teacher *tArray,int num)
    {
        Teacher tmp;
        for (int i = 0; i < num; i++)
        {
            for(int j = i+1;j < num; j++)
            {
                if(tArray[i].age > tArray[j].age)
                {
                    tmp = tArray[i];
                    tArray[i] = tArray[j];
                    tArray[j] = tmp;
                }
            }
        }
        return 0;
    }

    for(int i = 0;i < 3; i++)
    {
        printf("输入第%d个老师的年龄:\n",i+1);
        scanf("%d",&tArray[i].age);
    }
    printf("排序之前\n");
    printfTArray(tArray,3);
    sortTArray(tArray,3);
    printf("排序之后\n");
    printfTArray(tArray,3);
#endif
#if 0// 结构体数组分配数组在堆上
    typedef struct _Teacher
    {
        char name[62];// 4字节对齐
        int age;
    } Teacher;
    //
    int creatArray(Teacher *tArray,int num)
    {
        tArray = (Teacher *)malloc(num * sizeof(Teacher));
        if(tArray == NULL)
        {
            return -1;
        }
        return 0;
    }
    int creatArray2(Teacher **tArray,int num)
    {
        *tArray = (Teacher *)malloc(num * sizeof(Teacher));
        if(tArray == NULL)
        {
            return -1;
        }
        return 0;
    }
    int  printfTArray(Teacher *tArray,int num)
    {
        for (int i = 0; i < num; ++i)
        {
            printf("%d : age [%d]\n",i+1,tArray[i].age);
        }
        return 0;
    }
    int  sortTArray(Teacher *tArray,int num)
    {
        Teacher tmp;
        for (int i = 0; i < num; i++)
        {
            for(int j = i+1;j < num; j++)
            {
                if(tArray[i].age > tArray[j].age)
                {
                    tmp = tArray[i];
                    tArray[i] = tArray[j];
                    tArray[j] = tmp;
                }
            }
        }
        return 0;
    }
    Teacher *pArray = NULL;
    int ret;
    //ret = creatArray(pArray,3);// 这样分配不出来,修改指针
    ret = creatArray2(&pArray,3);
    if(ret != 0)
    {
        printf("creatArray NULL");
        return 0;
    }
    for(int i = 0;i < 3; i++)
    {
        printf("输入第%d个老师的年龄:\n",i+1);
        scanf("%d",&pArray[i].age);
    }

    printf("排序之前\n");
    printfTArray(pArray,3);
    sortTArray(pArray,3);
    printf("排序之后\n");
    printfTArray(pArray,3);
#endif
#if 0
    typedef struct _Teacher
    {
        char name[62];// 4字节对齐
        int age;
        char *title;
        char **pStuArray;
    } Teacher;
    Teacher *CreatArr(int num)
    {
        Teacher *tArray = NULL;
        tArray = (Teacher *)malloc(num * sizeof(Teacher));
        if(tArray == NULL)
        {
            return NULL;
        }

        for(int i = 0; i < num;i++)
        {
            tArray[i].title = (char *)malloc(100);
        }
        for(int i = 0; i < num;i++)
        {
            //            tArray[i].pStuArray = (char **)malloc(3*sizeof(char *));
            char **ptmp = (char **)malloc((3 + 1)*sizeof(char *));

            for(int j = 0; j < 3;j++)
            {
                ptmp[i] = (char *)malloc(100);
            }
            ptmp[i] = NULL;
            tArray[i].pStuArray = ptmp;//这种代码很难出错.
            // 假设 上面的3,接受过来3个,第4个赋值给p[i] = NULL
        }

    }

#endif
#if 0
    typedef struct _Teacher
    {
        char *name;// 4
        char buf[32];// 32字节对齐
        int age;
    } Teacher;
    int num = 0;
    Teacher *p = NULL;
    p = p - 1;
    p = p + 2;
    p = p - 4;
    p = p - p;
    // 可以编译通过,运行通过,是因为指针变量
    // ========
    (p->age);//
    &(p->age);// 可以通过编译,可以通过运行
    // & 属于cpu的计算,没有读写内存,所以没有当
    printf("============\n");

    num = (int)(&(p->age));// 36--> 4 +  32
    printf("num : [%d]\n",num);
    ((Teacher *)0)->age;//  &(p->age); 是一样的
    num = 0;
    num = (int)(&(((Teacher *)0)->age));// . ->是寻地址的
    // num 就是求相当于结构体的偏移量是固定的
    printf("num : [%d]\n",num);
    printf("============\n");
#endif
    return 0;
}
